package com.hb.unic.api.impl.aop;

import com.hb.unic.api.audit.service.IAuditLogService;
import com.hb.unic.api.impl.enums.AuditStatusEnum;
import com.hb.unic.core.async.threadpool.DefaultThreadPoolTaskOperation;
import com.hb.unic.core.expression.SpelMessageEvaluator;
import com.hb.unic.core.log.Audit;
import com.hb.unic.core.log.UnicLogger;
import com.hb.unic.core.log.UnicLoggerFactory;
import com.hb.unic.core.log.support.ILoggerConsts;
import com.hb.unic.core.util.RequestUtils;
import com.hb.unic.model.dobj.AuditLogDO;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.MDC;
import org.springframework.stereotype.Component;
import org.springframework.util.StopWatch;

import javax.annotation.Resource;
import java.lang.reflect.Method;
import java.util.Date;

/**
 * 审核日志切面
 *
 * @since v1.0，2023/8/31 19:51，create by Mr.Huang.
 */
@Aspect
@Component
public class AuditAspect {

    /**
     * log
     */
    private static final UnicLogger LOGGER = UnicLoggerFactory.getLogger(AuditAspect.class);

    /**
     * 默认线程池操作
     */
    @Resource
    private DefaultThreadPoolTaskOperation defaultThreadPoolTaskOperation;

    /**
     * 审核日志服务类
     */
    @Resource
    private IAuditLogService auditLogService;

    /**
     * 切面
     */
    @Pointcut("@annotation(com.hb.unic.core.log.Audit)")
    public void auditPointCut() {
    }

    /**
     * 周围通知
     *
     * @param point 调用参数
     * @return Object 方法调用结果
     * @throws Throwable 异常
     */
    @Around("auditPointCut()")
    public Object around(ProceedingJoinPoint point) throws Throwable {
        StopWatch sw = new StopWatch();
        sw.start();
        MethodSignature signature = (MethodSignature) point.getSignature();
        Method method = signature.getMethod();
        Audit audit = method.getAnnotation(Audit.class);
        AuditLogDO auditLogDO = new AuditLogDO();
        auditLogDO.setModule(audit.module());
        auditLogDO.setOperation(audit.operation());
        auditLogDO.setContent(SpelMessageEvaluator.evaluateString(audit.content(), method, point.getArgs()));
        auditLogDO.setStartTime(new Date());
        auditLogDO.setClientName("");
        auditLogDO.setClientIp(RequestUtils.getRequestIp());
        auditLogDO.setServerName(RequestUtils.getServiceName());
        auditLogDO.setServerIp(RequestUtils.getServiceIp());
        auditLogDO.setTraceId(MDC.get(ILoggerConsts.TRACE_ID));
        Object result = null;
        Exception ex = null;
        try {
            result = point.proceed();
            auditLogDO.setStatus(AuditStatusEnum.SUCCESS.getCode());
        } catch (Exception e) {
            ex = e;
            auditLogDO.setStatus(AuditStatusEnum.SUCCESS.getCode());
            auditLogDO.setErrorMsg(e.getMessage());
        }
        auditLogDO.setEndTime(new Date());
        auditLogDO.setCreateTime(new Date());
        auditLogDO.setUpdateTime(new Date());
        auditLogDO.setIsDelete(1);
        // 异步入库
        defaultThreadPoolTaskOperation.submitAll(() -> {
            auditLogService.insert(auditLogDO);
        });
        sw.stop();
        LOGGER.info("Audit work, cost={}ms", sw.getTotalTimeMillis());
        if (ex != null) {
            throw ex;
        }
        return result;
    }

}
